using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using Random = System.Random;

namespace Gj
{
    public interface TerrainElement
    {
        TerrainMesh[] GetMesh();
    }

    public class TerrainBase : MonoBehaviour
    {
        public int x = 5;
        public int y = 5;
        public bool random;
        public int randomSeed;
        public int type;
        public int xg = 6;
        public int zg = 6;
        public Vector3 center;

        public byte level;
        protected Bounds _navMeshBounds;
        protected NavMeshBuildSettings _navMeshBuildSettings;

        protected List<NavMeshBuildSource> _navMeshBuildSources;
        protected NavMeshData _navMeshData;
        protected NavMeshDataInstance _navMeshDataInstance;

        private NavMeshSurface _navMeshSurface;

        private bool _navNeedUpdate;
        private Action<int> process;
        public Random r;

        private NavMeshSurface NavMeshSurface
        {
            get
            {
                if (_navMeshSurface == null) _navMeshSurface = GetComponent<NavMeshSurface>();
                return _navMeshSurface;
            }
        }

        private void OnDestroy()
        {
            Hide();
        }

        protected void BuildProcess(int i)
        {
            process(i);
        }

        public void Create(int x, int y, Random r, int t, Action<int> b)
        {
            process = b;
            this.x = x;
            this.y = y;
            this.r = r;
            type = t;
            Create();
        }

        public void Create(Random r, int type, Action<int> b)
        {
            process = b;
            this.r = r;
            this.type = type;
            Create();
        }

        public void Create(Random r, Action<int> b)
        {
            process = b;
            this.r = r;
            Create();
        }

        public void Create(Random r)
        {
            this.r = r;
            Create();
        }
        
        

        protected virtual void OnAddBlock(Block block)
        {
            // yield break;
        }

        protected virtual void OnRemoveBlock(Block block)
        {
            // yield break;
        }

        protected virtual void OnUpdateBefore(Block block)
        {
            // yield break;
        }

        protected virtual void OnUpdateAfter(Block block)
        {
            // yield break;
        }

        protected virtual void OnKeep(Block block)
        {
            // yield break;
        }

        protected virtual void Create()
        {
            BlockTools.SyncManager.OnKeepEvent += OnKeep;
            BlockTools.SyncManager.OnAddBlockEvent += OnAddBlock;
            BlockTools.SyncManager.OnRemoveBlockEvent += OnRemoveBlock;
            BlockTools.SyncManager.OnUpdateBeforeEvent += OnUpdateBefore;
            BlockTools.SyncManager.OnUpdateAfterEvent += OnUpdateAfter;
            BlockTools.SyncManager.Init(xg * level, zg * level, xg * level / 2, zg * level / 2);
        }

        public void Open(Action<int> b)
        {
            process = b;
            r = GetRandom();
            Open();
        }

        protected virtual void Open()
        {
        }

        public virtual void ChangeLevel(byte level)
        {
            this.level = level;
            BlockTools.SyncManager.Update(xg * this.level, zg * this.level, xg * this.level / 2, zg * this.level / 2);
            BlockTools.SyncManager.UpdateCenter(center);
        }

        protected Random GetRandom()
        {
            if (random) return new Random();
            return new Random(randomSeed);
        }

        public virtual Vector3 GetRandomPoint(Random r)
        {
            return Vector3.zero;
        }

        public virtual Vector3 GetRandomPoint()
        {
            return GetRandomPoint(r);
        }

        public virtual Vector3 GetBirthPoint(int i)
        {
            return Vector3.zero;
        }

        protected virtual void UpdateCenter(Vector3 position)
        {
            var p = GetPoint(position);
            if (p != center)
            {
                center = p;
                BlockTools.SyncManager.UpdateCenter(center);
            }
        }

        protected void InitCenter(Vector3 position)
        {
            center = position;
            BlockTools.SyncManager.UpdateCenter(center);
        }

        private Vector3 GetPoint(Vector3 position)
        {
            // return new Vector3(Convert.ToInt32(position.x), 0, Convert.ToInt32(position.z));
            return MathTools.ToVector3(HexCube.FromPosition(position).ToOffset());
        }

        protected void GenerateNav()
        {
            var navMeshSurface = GetComponent<NavMeshSurface>();

            GetNavMeshSources();
            _navMeshBounds = MakeNavMeshBounds();
            if (_navMeshBounds.size == Vector3.zero)
                _navMeshBounds = NavMeshSurface.CalculateWorldBounds(_navMeshBuildSources);
            _navMeshBuildSettings = MakeNavMeshSetting(navMeshSurface.GetBuildSettings());

            _navMeshData = new NavMeshData();
            _navMeshData.position = transform.position;
            _navMeshData.rotation = transform.rotation;
        }

        protected void BuildNav()
        {
            GenerateNav();
            _navMeshData = NavMeshBuilder.BuildNavMeshData(_navMeshBuildSettings,
                _navMeshBuildSources, _navMeshBounds, transform.position, transform.rotation);
            Show();
        }

        public void AddNavMesh(NavMeshBuildSource source)
        {
            _navMeshBuildSources.Add(source);
        }

        public void RemoveNavMesh(NavMeshBuildSource source)
        {
            _navMeshBuildSources.Remove(source);
        }

        protected virtual NavMeshBuildSettings MakeNavMeshSetting(NavMeshBuildSettings settings)
        {
            return settings;
        }

        protected virtual List<NavMeshBuildSource> MakeNavMeshSources()
        {
            return null;
        }

        protected virtual Bounds MakeNavMeshBounds()
        {
            return new Bounds();
        }

        protected List<NavMeshBuildSource> GetNavMeshSources()
        {
            _navMeshBuildSources = MakeNavMeshSources();
            if (_navMeshBuildSources == null) _navMeshBuildSources = NavMeshSurface.CollectSources();
            return _navMeshBuildSources;
        }

        public void Hide()
        {
            NavMesh.RemoveNavMeshData(_navMeshDataInstance);
        }

        public void Show()
        {
            _navMeshDataInstance = NavMesh.AddNavMeshData(_navMeshData, transform.position, transform.rotation);
        }

        protected virtual void UpdateNavMeshData()
        {
        }

        protected AsyncOperation UpdateNavMesh()
        {
            UpdateNavMeshData();
            return NavMeshBuilder.UpdateNavMeshDataAsync(_navMeshData, _navMeshBuildSettings, _navMeshBuildSources,
                _navMeshBounds);
        }

        protected IEnumerator UpdateNavMeshAsync()
        {
            yield return UpdateNavMesh();
        }

        protected IEnumerator UpdateNavMeshAsyncLoop()
        {
            while (_navNeedUpdate) yield return UpdateNavMesh();
        }

        protected void UpdateNavMeshLoop()
        {
            _navNeedUpdate = true;
            StartCoroutine(UpdateNavMeshAsyncLoop());
        }
    }
}